<?php

/**
 * Github modules
 * @package modules
 * @subpackage github
 */

if (!defined('DEBUG_MODE')) { die(); }

/**
 * Used to cache "read" github items
 * @subpackage github/lib
 */
class Hm_Github_Uid_Cache {
    use Hm_Uid_Cache;
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_process_github_limit_setting extends Hm_Handler_Module {
    public function process() {
        process_site_setting('github_limit', $this, 'max_source_setting_callback', DEFAULT_GITHUB_PER_SOURCE);
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_process_github_since_setting extends Hm_Handler_Module {
    public function process() {
        process_site_setting('github_since', $this, 'since_setting_callback',DEFAULT_GITHUB_SINCE);
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_process_unread_github_included extends Hm_Handler_Module {
    public function process() {
        function unread_github_setting_callback($val) { return $val; }
        process_site_setting('unread_exclude_github', $this, 'unread_github_setting_callback', DEFAULT_UNREAD_EXCLUDE_GITHUB, true);
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_github_status extends Hm_Handler_Module {
    public function process() {
        list($success, $form) = $this->process_form(array('github_repo'));
        if ($success) {
            $details = $this->user_config->get('github_connect_details', array());
            if (!empty($details)) {
                $repos = $this->user_config->get('github_repos', array());
                if (in_array($form['github_repo'], $repos, true)) {
                    $url = sprintf('https://api.github.com/repos/%s/events?page=1&per_page=1', $form['github_repo']);
                    $start = microtime(true);
                    $api = new Hm_API_Curl();
                    $data = $api->command($url, array('Authorization: token ' . $details['access_token']));
                    if (!empty($data)) {
                        $this->out('github_status', 'success');
                        $this->out('github_connect_time', (microtime(true) - $start));
                        $this->out('github_repo', $form['github_repo']);
                        return;
                    }
                }
            }
            $this->out('github_status', 'failed');
            $this->out('github_repo', $form['github_repo']);
        }
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_load_github_repos extends Hm_Handler_Module {
    public function process() {
        $this->out('github_repos', $this->user_config->get('github_repos', array()));
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_github_message_action extends Hm_Handler_Module {
    public function process() {

        list($success, $form) = $this->process_form(array('action_type', 'message_ids'));
        if ($success) {
            $id_list = explode(',', $form['message_ids']);
            Hm_Github_Uid_Cache::load($this->cache->get('github_read_uids', array(), true));
            foreach ($id_list as $msg_id) {
                if (preg_match("/^github_(\d)+_(\d)+$/", $msg_id)) {
                    $parts = explode('_', $msg_id, 3);
                    $guid = $parts[2];
                    switch($form['action_type']) {
                        case 'unread':
                            Hm_Github_Uid_Cache::unread($guid);
                            break;
                        case 'read':
                        case 'delete':
                            Hm_Github_Uid_Cache::read($guid);
                            break;
                    }
                }
            }
            $this->cache->set('github_read_uids', Hm_Github_Uid_Cache::dump(), 0, true);
        }
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_github_folders_data extends Hm_Handler_Module {
    public function process() {
        $this->out('github_connect_details', $this->user_config->get('github_connect_details', array()));
        $this->out('github_repos', $this->user_config->get('github_repos', array()));
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_github_event_detail extends Hm_Handler_Module {
    public function process() {
        list($success, $form) = $this->process_form(array('list_path', 'github_uid'));
        if ($success) {
            Hm_Github_Uid_Cache::load($this->cache->get('github_read_uids', array(), true));
            $details = $this->user_config->get('github_connect_details', array());
            $repos = $this->user_config->get('github_repos', array());
            $limit = $this->user_config->get('github_limit_setting', DEFAULT_GITHUB_PER_SOURCE);
            $repo = mb_substr($form['list_path'], 7);
            if (in_array($repo, $repos, true)) {
                $url = sprintf('https://api.github.com/repos/%s/events?page=1&per_page='.$limit, $repo);
                $api = new Hm_API_Curl();
                $data = $api->command($url, array('Authorization: token ' . $details['access_token']));
                $event = array();
                $uid = explode('_', $form['github_uid'])[2];
                if (is_array($data)) {
                    foreach ($data as $item) {
                        if ($item['id'] == $uid) {
                            $event = $item;
                            break;
                        }
                    }
                }
                if (!empty($event)) {
                    Hm_Github_Uid_Cache::read($event['id']);
                    $this->cache->set('github_read_uids', Hm_Github_Uid_Cache::dump(), 0, true);

                    $page_title = build_github_subject($event, $this);
                    $this->out('page_title', $page_title);

                    $repo_parts = explode('/', urldecode($repo));
                    $repo_name = count($repo_parts) > 1 ? $repo_parts[1] : $repo;
                    $this->out('mailbox_list_title', array('Github', $repo_name));

                    $this->out('github_event_detail', $event);
                }
            }
        }
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_process_github_authorization extends Hm_Handler_Module {
    public function process() {
        if (array_key_exists('state', $this->request->get) && $this->request->get['state'] == 'github_authorization') {
            if (array_key_exists('code', $this->request->get)) {
                $details = github_connect_details($this->config);
                $oauth2 = new Hm_Oauth2($details['client_id'], $details['client_secret'], $details['redirect_uri']);
                $result = $oauth2->request_token($details['token_url'], $this->request->get['code'], array('Accept: application/json'));
                if (count($result) > 0 && array_key_exists('access_token', $result)) {
                    $this->load_repositories($result['access_token']);
                    Hm_Msgs::add('Github connection established', 'info');
                    $this->user_config->set('github_connect_details', $result);
                    $user_data = $this->user_config->dump();
                    $this->session->set('user_data', $user_data);
                    $this->session->record_unsaved('Github connection');
                    $this->session->secure_cookie($this->request, 'hm_reload_folders', '1');
                    $this->session->close_early();
                }
                else {
                    Hm_Msgs::add('An Error Occurred', 'danger');
                }
            }
            elseif (array_key_exists('error', $this->request->get)) {
                Hm_Msgs::add(''.ucwords(str_replace('_', ' ', $this->request->get['error'])), 'danger');
            }
            else {
                Hm_Msgs::add('An Error Occurred', 'danger');
            }
            $this->save_hm_msgs();
            Hm_Dispatch::page_redirect('?page=servers');
        }
    }

    public function load_repositories($token) {
        $repos = $this->user_config->get('github_repos', array());
        $url = sprintf('https://api.github.com/user/repos');
        $api = new Hm_API_Curl();
        $data = $api->command($url, array('Authorization: token ' . $token));
        foreach ($data as $value) {
            if (!in_array($value['full_name'], $repos)) {
                $repos[] = $value['full_name'];
            }
        }
        $this->user_config->set('github_repos', $repos);
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_setup_github_connect extends Hm_Handler_Module {
    public function process() {
        $details = github_connect_details($this->config);
        if (!empty($details)) {
            $oauth2 = new Hm_Oauth2($details['client_id'], $details['client_secret'], $details['redirect_uri']);
            $this->out('github_auth_url', $oauth2->request_authorization_url($details['auth_url'], 'repo', 'github_authorization'));
            $this->out('github_connect_details', $this->user_config->get('github_connect_details', array()));
            $this->out('github_repos', $this->user_config->get('github_repos', array()));
        }
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_github_process_remove_repo extends Hm_Handler_Module {
    public function process() {
        list($success, $form) = $this->process_form(array('github_repo', 'github_remove_repo'));
        if ($success) {
            $details = $this->user_config->get('github_connect_details');
            if ($details) {
                $repos = $this->user_config->get('github_repos', array());
                if (in_array($form['github_repo'], $repos, true)) {
                    foreach ($repos as $index => $repo) {
                        if ($repo === $form['github_repo']) {
                            unset($repos[$index]);
                            break;
                        }
                    }
                    $this->user_config->set('github_repos', $repos);
                    $user_data = $this->user_config->dump();
                    $this->session->set('user_data', $user_data);
                    $this->session->record_unsaved('Github repository removed');
                    $this->session->secure_cookie($this->request, 'hm_reload_folders', '1');
                    Hm_Msgs::add('Removed repository');
                }
            }
        }
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_github_process_add_repo extends Hm_Handler_Module {
    public function process() {
        list($success, $form) = $this->process_form(array('new_github_repo', 'new_github_repo_owner', 'github_add_repo'));
        if ($success) {
            $details = $this->user_config->get('github_connect_details');
            if ($details) {
                $url = sprintf('https://api.github.com/repos/%s/%s/events?page=1&per_page=1', urlencode($form['new_github_repo_owner']), urlencode($form['new_github_repo']));
                $api = new Hm_API_Curl();
                $data = $api->command($url, array('Authorization: token ' . $details['access_token']));
                if (!empty($data)) {
                    $repos = $this->user_config->get('github_repos', array());
                    $new_repo = urlencode($form['new_github_repo_owner']).'/'.urlencode($form['new_github_repo']);
                    if (!in_array($new_repo, $repos, true)) {
                        $repos[] = $new_repo;
                        $this->user_config->set('github_repos', $repos);
                        $user_data = $this->user_config->dump();
                        $this->session->set('user_data', $user_data);
                        $this->session->record_unsaved('Github repository added');
                        $this->session->secure_cookie($this->request, 'hm_reload_folders', '1');
                        Hm_Msgs::add('Added repository');
                    }
                    else {
                        Hm_Msgs::add('Repository is already added', 'warning');
                    }
                }
                else {
                    Hm_Msgs::add('Could not find that repository/owner combination at github.com', 'warning');
                }
            }
        }
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_github_disconnect extends Hm_Handler_Module {
    public function process() {
        if (array_key_exists('github_disconnect', $this->request->post)) {
            $this->user_config->set('github_connect_details', array());
            $user_data = $this->user_config->dump();
            $this->session->set('user_data', $user_data);
            $this->out('reload_folders', true, false);
            $this->session->record_unsaved('Github connection deleted');
            Hm_Msgs::add('Github connection deleted');
        }
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_github_list_data extends Hm_Handler_Module {
    public function process() {
        list($success, $form) = $this->process_form(array('github_repo'));
        if ($success) {
            $login_time = $this->session->get('login_time', false);
            if ($login_time) {
                $this->out('login_time', $login_time);
            }
            if (array_key_exists('list_path', $this->request->get)) {
                if (in_array($this->request->get['list_path'], array('combined_inbox', 'unread'), true)) {
                    $this->out('list_parent', $this->request->get['list_path']);
                }
            }
            Hm_Github_Uid_Cache::load($this->cache->get('github_read_uids', array(), true));
            $details = $this->user_config->get('github_connect_details');
            $repos = $this->user_config->get('github_repos');
            if (in_array($form['github_repo'], $repos, true) && $details) {
                $limit = $this->user_config->get('github_limit_setting', DEFAULT_GITHUB_PER_SOURCE);
                $url = sprintf('https://api.github.com/repos/%s/events?page=1&per_page='.$limit, $form['github_repo']);
                $api = new Hm_API_Curl();
                $this->out('github_data', $api->command($url, array('Authorization: token ' . $details['access_token'])));
                $this->out('github_data_source', $form['github_repo']);
                $this->out('github_data_source_id', array_search($form['github_repo'], $repos, true));
            }
            if (array_key_exists('list_path', $this->request->get)) {
                if ($this->request->get['list_path'] == 'unread') {
                    $this->out('github_list_since', process_since_argument($this->user_config->get('unread_since_setting', DEFAULT_UNREAD_SINCE)));
                }
                if ($this->request->get['list_path'] == 'combined_inbox') {
                    $this->out('github_list_since', process_since_argument($this->user_config->get('all_since_setting', DEFAULT_ALL_SINCE)));
                }
                if ($this->request->get['list_path'] == 'github_all') {
                    $this->out('github_list_since', process_since_argument($this->user_config->get('github_since_setting', DEFAULT_GITHUB_SINCE)));
                }
            }
            if (array_key_exists('github_unread', $this->request->post) && $this->request->post['github_unread']) {
                $this->out('github_unread', true);
            }
        }
    }
}

/**
 * @subpackage github/handler
 */
class Hm_Handler_github_list_type extends Hm_Handler_Module {
    public function process() {
        $repos = $this->user_config->get('github_repos', array());
        $excluded = $this->user_config->get('unread_exclude_github_setting', DEFAULT_UNREAD_EXCLUDE_GITHUB);
        $github_list = false;
        $parent = '';
        if (array_key_exists('list_parent', $this->request->get)) {
            $parent = $this->request->get['list_parent'];
        }
        if (array_key_exists('list_path', $this->request->get)) {
            $path = $this->request->get['list_path'];
            if ($this->page == 'message_list' && preg_match("/^github_(.+)$/", $path)) {
                $github_list = true;
                if ($path == 'github_all') {
                    $this->out('list_path', 'github_all', false);
                    $this->out('list_parent', $parent, false);
                    $this->out('mailbox_list_title', array('Github', 'All'));
                    $this->out('message_list_since', $this->user_config->get('github_since_setting', DEFAULT_GITHUB_SINCE));
                    $this->out('per_source_limit', $this->user_config->get('github_limit_setting', DEFAULT_GITHUB_PER_SOURCE));
                    foreach ($repos as $repo) {
                        $this->append('data_sources', array('type' => 'github', 'name' => $repo, 'id' => $repo));
                    }
                }
                else {
                    $repo = mb_substr($path, 7);
                    $this->out('list_parent', $parent, false);
                    if ($parent == 'github_all') {
                        $this->out('mailbox_list_title', array(urldecode($repo)));
                    }
                    else {
                        $this->out('mailbox_list_title', array('Github', urldecode($repo)));
                        $this->out('list_path', $path, false);
                    }
                    $this->out('custom_list_controls', ' ');
                    $this->append('data_sources', array('type' => 'github', 'name' => $repo, 'id' => $repo));
                }
            }
            elseif ($this->page == 'message_list' && ($path == 'combined_inbox' || $path == 'unread')) {
                $github_list = true;
                if (!$excluded || $path == 'combined_inbox') {
                    foreach ($repos as $repo) {
                        $this->append('data_sources', array('type' => 'github', 'name' => $repo, 'id' => $repo));
                    }
                }
            }
            elseif ($this->page == 'message' && preg_match("/^github_(.+)$/", $path)) {
                if (!$parent) {
                    $repo = mb_substr($path, 7);
                    $this->out('mailbox_list_title', array('Github', urldecode($repo)));
                }
            }
        }
        if (!$github_list) {
            foreach ($repos as $repo) {
                if (!$excluded) {
                    $this->append('data_sources', array('group' => 'background', 'type' => 'github', 'name' => 'Github', 'id' => $repo));
                }
            }
        }
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_github_folders extends Hm_Output_Module {
    protected function output() {
        $details = $this->get('github_connect_details', array());
        if (!empty($details)) {
            $res = '<li class="menu_github_all"><a class="unread_link" href="?page=message_list&list_path=github_all">';
            if (!$this->get('hide_folder_icons')) {
                $res .= '<i class="bi bi-code-slash account_icon"></i> ';
            }
            $res .= $this->trans('All').'</a></li>';
            foreach ($this->get('github_repos', array()) as $repo) {
                $res .= '<li class="menu_github_'.$this->html_safe($repo).'"><a class="unread_link" href="?page=message_list&list_path=github_'.$this->html_safe($repo).'">';
                if (!$this->get('hide_folder_icons')) {
                    $res .= '<i class="bi bi-code-slash account_icon"></i> ';
                }
                $res .= $this->html_safe(explode('/', urldecode($repo))[1]).'</a></li>';
            }
            $this->append('folder_sources', array('github_folders', $res));
        }
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_filter_github_data extends Hm_Output_Module {
    protected function output() {
        $res = array();
        $login_time = false;
        $unread_only = false;
        $show_icons = $this->get('msg_list_icons');
        if ($this->get('login_time')) {
            $login_time = $this->get('login_time');
        }
        if ($this->get('list_path', '') == 'unread' || $this->get('github_unread', false)) {
            $unread_only = true;
        }
        $repo_id = $this->get('github_data_source_id');
        $repo = $this->get('github_data_source', 'Github');
        $repo_parts = explode('/', $repo);
        $repo_name = $repo_parts[1];
        $cutoff = $this->get('github_list_since', '');
        if ($cutoff) {
            $cutoff = strtotime($cutoff);
        }
        else {
            $cutoff = 0;
        }
        $list_parent = false;
        if ($this->get('list_parent', '')) {
            $list_parent = $this->get('list_parent');
        }
        elseif ($this->get('list_path') == 'github_all') {
            $list_parent = $this->get('list_path');
        }
        foreach ($this->get('github_data', array()) as $event) {
            if (!is_array($event) || !array_key_exists('id', $event)) {
                continue;
            }
            $row_class = 'github';
            $id = 'github_'.$repo_id.'_'.$event['id'];
            $subject = build_github_subject($event, $this);
            $url = '?page=message&uid='.$this->html_safe($id).'&list_path=github_'.$this->html_safe($repo);
            if ($list_parent) {
                $url .= '&list_parent='.$this->html_safe($list_parent);
            }
            $from = $event['actor']['login'];
            $ts = strtotime($event['created_at']);
            if ($ts < $cutoff) {
                continue;
            }
            if (Hm_Github_Uid_Cache::is_read($event['id'])) {
                $flags = array();
            }
            elseif (Hm_Github_Uid_Cache::is_unread($event['id'])) {
                $flags = array('unseen');
                $row_class .= ' unseen';
            }
            elseif ($ts && $login_time && $ts <= $login_time) {
                $flags = array();
            }
            else {
                $row_class .= ' unseen';
                $flags = array('unseen');
            }
            if ($unread_only && !in_array('unseen', $flags)) {
                continue;
            }
            $date = date('r', $ts);
            $style = $this->get('news_list_style') ? 'news' : 'email';
            if ($this->get('is_mobile')) {
                $style = 'news';
            }
            $row_class .= ' '.$repo_name;
            $icon = 'code';
            if (!$show_icons) {
                $icon = '';
            }
            if ($style == 'news') {
                $res[$id] = message_list_row(array(
                        array('checkbox_callback', $id),
                        array('icon_callback', $flags),
                        array('subject_callback', $subject, $url, $flags, $icon),
                        array('safe_output_callback', 'source', $repo_name),
                        array('safe_output_callback', 'from', $from),
                        array('date_callback', translate_time_str(human_readable_interval($date), $this), $ts),
                    ),
                    $id,
                    $style,
                    $this,
                    $row_class
                );
            }
            else {
                $res[$id] = message_list_row(array(
                        array('checkbox_callback', $id),
                        array('safe_output_callback', 'source', $repo_name, $icon),
                        array('safe_output_callback', 'from', $from),
                        array('subject_callback', $subject, $url, $flags),
                        array('date_callback', translate_time_str(human_readable_interval($date), $this), $ts),
                        array('icon_callback', $flags)
                    ),
                    $id,
                    $style,
                    $this,
                    $row_class
                );
            }
        }
        $this->out('formatted_message_list', $res);
        $this->out('github_server_id', $repo_id);
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_github_add_repo extends Hm_Output_Module {
    protected function output() {
        $res = '';
        $details = $this->get('github_connect_details', array());
        if (!empty($details)) {
            $res = '<div class="configured_server col-12 col-lg-4 mb-4 mt-3">
                <div class="subtitle">'.$this->trans('Add a Repository').'</div>'.
                '<form method="POST">'.
                '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />'.
                '<label class="screen_reader">'.$this->trans('Name').'</label>'.
                '<input type="text" value="" class="form-control mb-4" placeholder="'.$this->trans('Name').'" name="new_github_repo" />'.
                '<label class="screen_reader">'.$this->trans('Owner').'</label>'.
                '<input type="text" value="" class="form-control mb-4" placeholder="'.$this->trans('Owner').'" name="new_github_repo_owner" />'.
                '<input type="submit" class="btn btn-primary" name="github_add_repo" value="Add" />'.
                '</form></div>';
            $res .= '<div class="configured_server mt-2"><h5 class="server_title">'.$this->trans('Repositories').'</h5></div>'.
                '<table class="table">';
            foreach ($this->get('github_repos', array()) as $repo) {
                $res .= '<tr>'.
                    '<td class="col-sm-2 col-lg-1 py-2 align-middle">'.
                        '<form class="remove_repo" method="POST">'.
                            '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />'.
                            '<input type="hidden" name="github_repo" value="'.$this->html_safe($repo).'" />'.
                            '<input type="submit" name="github_remove_repo" value="'.$this->trans('Remove').'" class="github_remove_repo btn btn-outline-danger btn-sm" />'.
                        '</form>'.
                    '</td>'.
                    '<td>'.$this->html_safe($repo).'</td>'.
                '<tr>';
            }
            $res .= '</table></div></div><div class="end_float"></div></div>';
        }
        return $res;
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_filter_github_event_detail extends Hm_Output_Module {
    protected function output() {
        $details = $this->get('github_event_detail', array());
        if (!empty($details)) {
            if (array_key_exists('payload', $details)) {
                $body = github_parse_payload($details, $this);
                $headers = github_parse_headers($details, $this);
            }
            $this->out('github_msg_text', $headers.$body);
        }
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_filter_github_status extends Hm_Output_Module {
    protected function output() {
        if ($this->get('github_status') == 'success') {
            $this->out('github_status_display', '<span class="online">'.$this->trans('Connected').'</span> in '.round($this->get('github_connect_time'), 3));
        }
        else {
            $this->out('github_status_display', '<span class="down">'.$this->trans('Down').'</span>');
        }
        $this->out('github_status_repo', $this->get('github_repo', ''));
    }
};

/**
 * @subpackage github/output
 */
class Hm_Output_display_github_status extends Hm_Output_Module {
    protected function output() {
        $res = '';
        $repos = $this->get('github_repos', array());
        if (!empty($repos)) {
            foreach ($repos as $repo) {
                $res .= '<tr><td class="github_repo" data-id="'.$this->html_safe($repo).'">'.$this->trans('Github repo').'</td><td>'.$this->html_safe($repo).'</td><td class="github_'.$this->html_safe($repo).'"></td></tr>';
            }
        }
        return $res;
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_github_connect_section extends Hm_Output_Module {
    protected function output() {
        $details = $this->get('github_connect_details', array());
        $res = '<div class="github_connect"><div data-target=".github_connect_section" class="server_section border-bottom cursor-pointer px-1 py-3 pe-auto">
                    <a href="#" class="pe-auto">
                        <i class="bi bi-github me-3"></i>
                        <b>'.$this->trans('Github Connect').'</b>
                    </a>
                </div>';

        $res .= '<div class="github_connect_section"><div class="add_server">';

        if (empty($details)) {
            $res .= 'Connect to Github<br /><br />';
            $res .= '<a class="btn btn-secondary" href="'.$this->get('github_auth_url', '').'" data-external="true">'.$this->trans('Enable').'</a></div></div><div class="end_float"</div>';
        }
        else {
            $res .='<h6>'. $this->trans('Already connected').'</h6>';
            $res .= '<form id="github_disconnect_form" method="POST">';
            $res .= '<input type="hidden" name="hm_page_key" value="'.$this->html_safe(Hm_Request_Key::generate()).'" />';
            $res .= '<input type="submit" name="github_disconnect" class="github_disconnect btn btn-secondary" value="'.$this->trans('Disconnect').'" />';
            $res .= '</form>';
        }
        return $res.'</div>';
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_unread_github_included_setting extends Hm_Output_Module {
    protected function output() {
        $settings = $this->get('user_settings');
        if (array_key_exists('unread_exclude_github', $settings) && $settings['unread_exclude_github']) {
            $checked = ' checked="checked"';
        }
        else {
            $checked = '';
        }
        return '<tr class="unread_setting"><td><label for="unread_exclude_github">'.$this->trans('Exclude unread Github notices').'</label></td>'.
            '<td><input class="form-check-input" type="checkbox" '.$checked.' value="1" id="unread_exclude_github" name="unread_exclude_github" /></td></tr>';
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_start_github_settings extends Hm_Output_Module {
    protected function output() {
        return '<tr><td colspan="2" data-target=".github_all_setting" class="settings_subtitle cursor-pointer border-bottom p-2">'.
            '<i class="bi bi-github fs-5 me-2"></i>'.$this->trans('Github Settings').'</td></tr>';
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_github_since_setting extends Hm_Output_Module {
    protected function output() {
        $since = DEFAULT_GITHUB_SINCE;
        $settings = $this->get('user_settings');
        if (array_key_exists('github_since', $settings) && $settings['github_since']) {
            $since = $settings['github_since'];
        }
        return '<tr class="github_all_setting"><td><label for="github_since">'.$this->trans('Show Github notices received since').'</label></td>'.
            '<td>'.message_since_dropdown($since, 'github_since', $this, DEFAULT_GITHUB_SINCE).'</td></tr>';
    }
}

/**
 * @subpackage github/output
 */
class Hm_Output_github_limit_setting extends Hm_Output_Module {
    protected function output() {
        $limit = DEFAULT_GITHUB_PER_SOURCE;
        $settings = $this->get('user_settings');
        if (array_key_exists('github_limit', $settings)) {
            $limit = $settings['github_limit'];
        }
        return '<tr class="github_all_setting"><td><label for="github_limit">'.$this->trans('Max Github notices per repository').'</label></td>'.
            '<td class="d-flex"><input type="text" class="form-control form-control-sm w-auto" id="github_limit" name="github_limit" size="2" value="'.$this->html_safe($limit).'" data-default-value="'.DEFAULT_GITHUB_PER_SOURCE.'"/></td></tr>';
    }
}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_connect_details')) {
function github_connect_details($config) {
    $confs = $config->dump();
    return $confs['github'];
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('build_github_subject')) {
function build_github_subject($event, $output_mod) {
    $ref = '';
    if (array_key_exists('payload', $event) && array_key_exists('ref', $event['payload'])) {
        $branch_name = preg_replace("/^refs\/heads\//", '', $event['payload']['ref']);
        $clean_branch = html_entity_decode($branch_name, ENT_QUOTES | ENT_HTML401, 'UTF-8');
        $ref = sprintf(' (%s)', $clean_branch);
    }

    $type = mb_strtolower($event['type']);
    $post = '';
    $max = 80;
    $repo_name = isset($event['repo']['name']) ? $event['repo']['name'] : 'Repository';
    $actor = isset($event['actor']['login']) ? $event['actor']['login'] : 'Someone';

    switch ($type) {
        case 'pushevent':
            $commit_count = count($event['payload']['commits']);
            if ($commit_count > 1) {
                $post = sprintf('%d new commits to %s', $commit_count, $repo_name);
                if ($ref) {
                    $post .= $ref;
                }
            } else {
                $commit_msg = html_entity_decode($event['payload']['commits'][0]['message'], ENT_QUOTES | ENT_HTML401, 'UTF-8');
                $commit_msg = mb_substr($commit_msg, 0, $max);
                $post = sprintf('New commit: %s', $commit_msg);
                if ($ref) {
                    $post .= $ref;
                }
            }
            break;

        case 'issuesevent':
            $issue_title = html_entity_decode($event['payload']['issue']['title'], ENT_QUOTES | ENT_HTML401, 'UTF-8');
            $issue_title = mb_substr($issue_title, 0, $max);
            $action = ucfirst($event['payload']['action']);
            $issue_number = $event['payload']['issue']['number'];
            $post = sprintf('Issue #%d %s: %s', $issue_number, strtolower($action), $issue_title);
            break;

        case 'issuecommentevent':
            $issue_title = html_entity_decode($event['payload']['issue']['title'], ENT_QUOTES | ENT_HTML401, 'UTF-8');
            $issue_title = mb_substr($issue_title, 0, $max);
            $issue_number = $event['payload']['issue']['number'];
            $post = sprintf('Comment on issue #%d: %s', $issue_number, $issue_title);
            break;

        case 'pullrequestevent':
            $pr_title = html_entity_decode($event['payload']['pull_request']['title'], ENT_QUOTES | ENT_HTML401, 'UTF-8');
            $pr_title = mb_substr($pr_title, 0, $max);
            $action = ucfirst($event['payload']['action']);
            $pr_number = $event['payload']['pull_request']['number'];
            $post = sprintf('Pull request #%d %s: %s', $pr_number, strtolower($action), $pr_title);
            break;

        case 'pullrequestcommentevent':
        case 'pullrequestreviewcommentevent':
            $pr_title = mb_substr($event['payload']['pull_request']['title'], 0, $max);
            $pr_number = $event['payload']['pull_request']['number'];
            $post = sprintf('Comment on pull request #%d: %s', $pr_number, $pr_title);
            break;

        case 'releaseevent':
            $release_name = mb_substr($event['payload']['release']['name'] ?: $event['payload']['release']['tag_name'], 0, $max);
            $action = ucfirst($event['payload']['action']);
            $post = sprintf('Release %s: %s', strtolower($action), $release_name);
            break;

        case 'createevent':
            $ref_type = $event['payload']['ref_type'];
            if ($ref_type === 'repository') {
                $post = sprintf('Repository %s created', $repo_name);
            } elseif ($ref_type === 'branch') {
                $branch_name = html_entity_decode($event['payload']['ref'], ENT_QUOTES | ENT_HTML401, 'UTF-8');
                $post = sprintf('Branch %s created in %s', $branch_name, $repo_name);
            } elseif ($ref_type === 'tag') {
                $tag_name = html_entity_decode($event['payload']['ref'], ENT_QUOTES | ENT_HTML401, 'UTF-8');
                $post = sprintf('Tag %s created in %s', $tag_name, $repo_name);
            } else {
                $post = sprintf('%s %s created in %s', ucfirst($ref_type), $event['payload']['ref'], $repo_name);
            }
            break;

        case 'deleteevent':
            $ref_type = $event['payload']['ref_type'];
            $ref_name = html_entity_decode($event['payload']['ref'], ENT_QUOTES | ENT_HTML401, 'UTF-8');
            $post = sprintf('%s %s deleted in %s', ucfirst($ref_type), $ref_name, $repo_name);
            break;

        case 'forkevent':
            $fork_name = $event['payload']['forkee']['full_name'];
            $post = sprintf('Repository forked to %s', $fork_name);
            break;

        case 'watchevent':
            if ($event['payload']['action'] == 'started') {
                $post = sprintf('%s started watching %s', $actor, $repo_name);
            } else {
                $post = sprintf('%s stopped watching %s', $actor, $repo_name);
            }
            break;

        case 'commitcommentevent':
            $commit_sha = mb_substr($event['payload']['comment']['commit_id'], 0, 7);
            $post = sprintf('Comment on commit %s in %s', $commit_sha, $repo_name);
            break;

        case 'gollumwikievent':
        case 'gollumevent':
            $page_count = count($event['payload']['pages']);
            if ($page_count > 1) {
                $post = sprintf('%d wiki pages updated in %s', $page_count, $repo_name);
            } else {
                $page_name = $event['payload']['pages'][0]['page_name'];
                $action = $event['payload']['pages'][0]['action'];
                $post = sprintf('Wiki page "%s" %s in %s', $page_name, $action, $repo_name);
            }
            break;

        case 'memberevent':
            $member = $event['payload']['member']['login'];
            $action = $event['payload']['action'];
            $post = sprintf('Member %s %s in %s', $member, $action, $repo_name);
            break;

        default:
            $event_name = preg_replace("/Event$/", "", $event['type']);
            $event_name = preg_replace("/([A-Z])/", " $1", $event_name);
            $event_name = trim($event_name);
            $post = sprintf('%s in %s', $event_name, $repo_name);
            break;
    }

    $post = html_entity_decode($post, ENT_QUOTES | ENT_HTML401, 'UTF-8');

    return $output_mod->html_safe($post);
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_parse_headers')) {
function github_parse_headers($data, $output_mod) {
    $res = '<div class="msg_headers d-flex flex-column border-bottom border-2 border-secondary-subtle pb-3 mb-3">';

    if (array_key_exists('type', $data)) {
        $subject = build_github_subject($data, $output_mod);
        $event_type = github_get_event_type_display($data['type']);
        $event_icon = github_get_event_icon($data['type']);
    } else {
        $subject = '[Unknown Event]';
        $event_type = 'Unknown';
        $event_icon = 'bi-question-circle';
    }

    if (array_key_exists('created_at', $data)) {
        $timestamp = strtotime($data['created_at']);
        $date = sprintf("%s (%s ago)", date('M j, Y g:i A', $timestamp), human_readable_interval($data['created_at']));
    } else {
        $date = '[No date]';
    }

    $from = '[Unknown User]';
    $from_link = '';
    if (array_key_exists('actor', $data) && array_key_exists('login', $data['actor'])) {
        $from = $data['actor']['login'];
        $from_link = sprintf('<a href="https://github.com/%s" target="_blank" rel="noopener" class="text-decoration-none">@%s</a>',
            $output_mod->html_safe($from), $output_mod->html_safe($from));
    }

    $repo_name = '[Unknown Repository]';
    $repo_link = '';
    if (array_key_exists('repo', $data) && array_key_exists('name', $data['repo'])) {
        $repo_name = $data['repo']['name'];
        $repo_link = sprintf('<a href="https://github.com/%s" target="_blank" rel="noopener" class="text-decoration-none">%s</a>',
            $output_mod->html_safe($repo_name), $output_mod->html_safe($repo_name));
    }

    $event_link = github_get_event_link($data, $output_mod);

    $res .= '<div class="header_subject d-flex justify-content-center align-items-center mb-3">';
    $res .= '<i class="bi '.$event_icon.' fs-4 me-2 text-primary"></i>';
    $res .= '<h4 class="text-center mb-0 fw-bold">'.$output_mod->html_safe($subject).'</h4>';
    $res .= '</div>';

    $res .= '<div class="d-flex align-items-center py-1">';
    $res .= '<span class="fw-semibold me-2 text-nowrap">'.$output_mod->trans('Event Type').':</span>';
    $res .= '<span class="badge bg-secondary">'.$output_mod->html_safe($event_type).'</span>';
    $res .= '</div>';

    $res .= '<div class="header_date d-flex align-items-center py-1">';
    $res .= '<span class="fw-semibold me-2 text-nowrap">'.$output_mod->trans('Date').':</span>';
    $res .= '<span class="text-break">'.$output_mod->html_safe($date).'</span>';
    $res .= '</div>';

    $res .= '<div class="header_from d-flex align-items-center py-1">';
    $res .= '<span class="fw-semibold me-2 text-nowrap">'.$output_mod->trans('Author').':</span>';
    $res .= '<span class="text-break">'.$from_link.'</span>';
    $res .= '</div>';

    $res .= '<div class="d-flex align-items-center py-1">';
    $res .= '<span class="fw-semibold me-2 text-nowrap">'.$output_mod->trans('Repository').':</span>';
    $res .= '<span class="text-break">'.$repo_link.'</span>';
    $res .= '</div>';

    if ($event_link) {
        $res .= '<div class="d-flex align-items-center py-1">';
        $res .= '<span class="fw-semibold me-2 text-nowrap">'.$output_mod->trans('View on GitHub').':</span>';
        $res .= '<span class="text-break">'.$event_link.'</span>';
        $res .= '</div>';
    }

    $res .= '</div>';
    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_parse_payload')) {
function github_parse_payload($data, $output_mod) {
    $type = $data['type'] ?? 'Unknown';
    $payload = $data['payload'] ?? array();

    $res = '<div class="msg_text_inner">';

    switch ($type) {
        case 'PushEvent':
            $res .= github_format_push_event($payload, $output_mod);
            break;

        case 'IssuesEvent':
            $res .= github_format_issue_event($payload, $output_mod);
            break;

        case 'IssueCommentEvent':
            $res .= github_format_issue_comment_event($payload, $output_mod);
            break;

        case 'PullRequestEvent':
            $res .= github_format_pull_request_event($payload, $output_mod);
            break;

        case 'PullRequestCommentEvent':
        case 'PullRequestReviewCommentEvent':
            $res .= github_format_pull_request_comment_event($payload, $output_mod);
            break;

        case 'ReleaseEvent':
            $res .= github_format_release_event($payload, $output_mod);
            break;

        case 'CreateEvent':
        case 'DeleteEvent':
            $res .= github_format_create_delete_event($payload, $type, $output_mod);
            break;

        case 'ForkEvent':
            $res .= github_format_fork_event($payload, $output_mod);
            break;

        case 'WatchEvent':
            $res .= github_format_watch_event($payload, $output_mod);
            break;

        case 'CommitCommentEvent':
            $res .= github_format_commit_comment_event($payload, $output_mod);
            break;

        case 'GollumEvent':
            $res .= github_format_wiki_event($payload, $output_mod);
            break;

        case 'MemberEvent':
            $res .= github_format_member_event($payload, $output_mod);
            break;

        default:
            // Fallback to generic parsing for unknown events
            $content = payload_search($payload);
            foreach ($content as $vals) {
                $res .= '<div class="github_para">';
                if (array_key_exists('name', $vals)) {
                    $res .= '<strong>'.$output_mod->html_safe($vals['name']).'</strong><br>';
                }
                if (array_key_exists('body', $vals)) {
                    $res .= $output_mod->html_safe(wordwrap($vals['body'], 100)).'<br>';
                }
                if (array_key_exists('message', $vals)) {
                    $res .= $output_mod->html_safe(wordwrap($vals['message'], 100)).'<br>';
                }
                if (array_key_exists('html_url', $vals)) {
                    $res .= sprintf('<a href="%s" target="_blank" rel="noopener" class="btn btn-sm btn-primary text-white mt-2">View on GitHub</a>',
                        $output_mod->html_safe($vals['html_url']));
                }
                $res .= '</div>';
            }
            break;
    }

    $res .= '</div>';
    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_get_event_type_display')) {
function github_get_event_type_display($type) {
    $display_names = array(
        'PushEvent' => 'Push',
        'IssuesEvent' => 'Issue',
        'IssueCommentEvent' => 'Issue Comment',
        'PullRequestEvent' => 'Pull Request',
        'PullRequestCommentEvent' => 'PR Comment',
        'PullRequestReviewCommentEvent' => 'PR Review',
        'ReleaseEvent' => 'Release',
        'CreateEvent' => 'Create',
        'DeleteEvent' => 'Delete',
        'ForkEvent' => 'Fork',
        'WatchEvent' => 'Watch',
        'CommitCommentEvent' => 'Commit Comment',
        'GollumEvent' => 'Wiki',
        'MemberEvent' => 'Member'
    );
    return isset($display_names[$type]) ? $display_names[$type] : preg_replace("/Event$/", "", $type);
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_get_event_icon')) {
function github_get_event_icon($type) {
    $icons = array(
        'PushEvent' => 'bi-arrow-up-circle',
        'IssuesEvent' => 'bi-exclamation-circle',
        'IssueCommentEvent' => 'bi-chat-left-text',
        'PullRequestEvent' => 'bi-git',
        'PullRequestCommentEvent' => 'bi-chat-left-dots',
        'PullRequestReviewCommentEvent' => 'bi-chat-left-quote',
        'ReleaseEvent' => 'bi-tag',
        'CreateEvent' => 'bi-plus-circle',
        'DeleteEvent' => 'bi-dash-circle',
        'ForkEvent' => 'bi-diagram-3',
        'WatchEvent' => 'bi-eye',
        'CommitCommentEvent' => 'bi-chat-square-text',
        'GollumEvent' => 'bi-journal-text',
        'MemberEvent' => 'bi-person-plus'
    );
    return isset($icons[$type]) ? $icons[$type] : 'bi-code-slash';
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_get_event_link')) {
function github_get_event_link($data, $output_mod) {
    if (!isset($data['type']) || !isset($data['repo']['name'])) {
        return '';
    }

    $repo_name = $data['repo']['name'];
    $type = $data['type'];
    $link = '';
    $link_text = '';

    switch ($type) {
        case 'IssuesEvent':
        case 'IssueCommentEvent':
            if (isset($data['payload']['issue']['html_url'])) {
                $link = $data['payload']['issue']['html_url'];
                $link_text = sprintf('Issue #%d', $data['payload']['issue']['number']);
            }
            break;

        case 'PullRequestEvent':
        case 'PullRequestCommentEvent':
        case 'PullRequestReviewCommentEvent':
            if (isset($data['payload']['pull_request']['html_url'])) {
                $link = $data['payload']['pull_request']['html_url'];
                $link_text = sprintf('Pull Request #%d', $data['payload']['pull_request']['number']);
            }
            break;

        case 'ReleaseEvent':
            if (isset($data['payload']['release']['html_url'])) {
                $link = $data['payload']['release']['html_url'];
                $link_text = sprintf('Release %s', $data['payload']['release']['tag_name']);
            }
            break;

        case 'CommitCommentEvent':
            if (isset($data['payload']['comment']['html_url'])) {
                $link = $data['payload']['comment']['html_url'];
                $link_text = sprintf('Commit %s', substr($data['payload']['comment']['commit_id'], 0, 7));
            }
            break;

        case 'PushEvent':
            if (isset($data['payload']['commits']) && count($data['payload']['commits']) > 0) {
                $commit = $data['payload']['commits'][0];
                $link = sprintf('https://github.com/%s/commit/%s', $repo_name, $commit['sha']);
                $link_text = sprintf('Commit %s', substr($commit['sha'], 0, 7));
            }
            break;
    }

    if ($link && $link_text) {
        return sprintf('<a href="%s" target="_blank" rel="noopener" class="btn btn-sm btn-primary text-white">%s</a>',
            $output_mod->html_safe($link), $output_mod->html_safe($link_text));
    }

    return '';
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_push_event')) {
function github_format_push_event($payload, $output_mod) {
    $res = '';
    $commits = $payload['commits'] ?? array();
    $ref = $payload['ref'] ?? '';
    $branch = preg_replace("/^refs\/heads\//", '', $ref);

    if (!empty($commits)) {
        $res .= '<div class="alert alert-info">';
        $res .= '<h5><i class="bi bi-arrow-up-circle me-2"></i>'.count($commits).' commit(s) pushed to <code>'.$output_mod->html_safe($branch).'</code></h5>';
        $res .= '</div>';

        foreach ($commits as $commit) {
            $res .= '<div class="card mb-3">';
            $res .= '<div class="card-body">';
            $res .= '<h6 class="card-title"><code>'.substr($commit['sha'], 0, 7).'</code></h6>';
            $res .= '<p class="card-text">'.$output_mod->html_safe($commit['message']).'</p>';
            if (isset($commit['author']['name'])) {
                $res .= '<small class="text-muted">by '.$output_mod->html_safe($commit['author']['name']).'</small>';
            }
            if (isset($commit['url'])) {
                $github_url = str_replace(array('commits', 'https://api.github.com/repos'), array('commit', 'https://github.com'), $commit['url']);
                $res .= '<br><a href="'.$output_mod->html_safe($github_url).'" target="_blank" rel="noopener" class="btn btn-sm btn-primary text-white mt-2">View Commit</a>';
            }
            $res .= '</div></div>';
        }
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_issue_event')) {
function github_format_issue_event($payload, $output_mod) {
    $issue = $payload['issue'] ?? array();
    $action = $payload['action'] ?? 'unknown';

    $res = '<div class="alert alert-'.($action === 'opened' ? 'success' : ($action === 'closed' ? 'secondary' : 'info')).'">';
    $res .= '<h5><i class="bi bi-exclamation-circle me-2"></i>Issue '.ucfirst($action).'</h5>';
    $res .= '</div>';

    if (!empty($issue)) {
        $res .= '<div class="card">';
        $res .= '<div class="card-body">';
        $res .= '<h5 class="card-title">'.$output_mod->html_safe($issue['title']).'</h5>';
        $res .= '<h6 class="card-subtitle mb-2 text-muted">Issue #'.$issue['number'].'</h6>';
        if (isset($issue['body']) && $issue['body']) {
            $res .= '<p class="card-text">'.$output_mod->html_safe(mb_substr($issue['body'], 0, 500)).'</p>';
        }
        if (isset($issue['html_url'])) {
            $res .= '<a href="'.$output_mod->html_safe($issue['html_url']).'" target="_blank" rel="noopener" class="btn btn-primary text-white">View Issue</a>';
        }
        $res .= '</div></div>';
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_issue_comment_event')) {
function github_format_issue_comment_event($payload, $output_mod) {
    $issue = $payload['issue'] ?? array();
    $comment = $payload['comment'] ?? array();

    $res = '<div class="alert alert-info">';
    $res .= '<h5><i class="bi bi-chat-left-text me-2"></i>New Comment on Issue</h5>';
    $res .= '</div>';

    if (!empty($issue)) {
        $res .= '<div class="mb-3">';
        $res .= '<h6>Issue: '.$output_mod->html_safe($issue['title']).' (#'.$issue['number'].')</h6>';
        $res .= '</div>';
    }

    if (!empty($comment)) {
        $res .= '<div class="card">';
        $res .= '<div class="card-body">';
        $res .= '<p class="card-text">'.$output_mod->html_safe(mb_substr($comment['body'], 0, 500)).'</p>';
        if (isset($comment['html_url'])) {
            $res .= '<a href="'.$output_mod->html_safe($comment['html_url']).'" target="_blank" rel="noopener" class="btn btn-primary text-white">View Comment</a>';
        }
        $res .= '</div></div>';
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_pull_request_event')) {
function github_format_pull_request_event($payload, $output_mod) {
    $pr = $payload['pull_request'] ?? array();
    $action = $payload['action'] ?? 'unknown';

    $res = '<div class="alert alert-'.($action === 'opened' ? 'success' : ($action === 'closed' ? 'secondary' : 'info')).'">';
    $res .= '<h5><i class="bi bi-git me-2"></i>Pull Request '.ucfirst($action).'</h5>';
    $res .= '</div>';

    if (!empty($pr)) {
        $res .= '<div class="card">';
        $res .= '<div class="card-body">';
        $res .= '<h5 class="card-title">'.$output_mod->html_safe($pr['title']).'</h5>';
        $res .= '<h6 class="card-subtitle mb-2 text-muted">Pull Request #'.$pr['number'].'</h6>';
        if (isset($pr['body']) && $pr['body']) {
            $res .= '<p class="card-text">'.$output_mod->html_safe(mb_substr($pr['body'], 0, 500)).'</p>';
        }
        if (isset($pr['html_url'])) {
            $res .= '<a href="'.$output_mod->html_safe($pr['html_url']).'" target="_blank" rel="noopener" class="btn btn-primary text-white">View Pull Request</a>';
        }
        $res .= '</div></div>';
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_pull_request_comment_event')) {
function github_format_pull_request_comment_event($payload, $output_mod) {
    $pr = $payload['pull_request'] ?? array();
    $comment = $payload['comment'] ?? array();

    $res = '<div class="alert alert-info">';
    $res .= '<h5><i class="bi bi-chat-left-dots me-2"></i>New Comment on Pull Request</h5>';
    $res .= '</div>';

    if (!empty($pr)) {
        $res .= '<div class="mb-3">';
        $res .= '<h6>Pull Request: '.$output_mod->html_safe($pr['title']).' (#'.$pr['number'].')</h6>';
        $res .= '</div>';
    }

    if (!empty($comment)) {
        $res .= '<div class="card">';
        $res .= '<div class="card-body">';
        $res .= '<p class="card-text">'.$output_mod->html_safe(mb_substr($comment['body'], 0, 500)).'</p>';
        if (isset($comment['html_url'])) {
            $res .= '<a href="'.$output_mod->html_safe($comment['html_url']).'" target="_blank" rel="noopener" class="btn btn-primary text-white">View Comment</a>';
        }
        $res .= '</div></div>';
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_release_event')) {
function github_format_release_event($payload, $output_mod) {
    $release = $payload['release'] ?? array();
    $action = $payload['action'] ?? 'published';

    $res = '<div class="alert alert-success">';
    $res .= '<h5><i class="bi bi-tag me-2"></i>Release '.ucfirst($action).'</h5>';
    $res .= '</div>';

    if (!empty($release)) {
        $res .= '<div class="card">';
        $res .= '<div class="card-body">';
        $res .= '<h5 class="card-title">'.$output_mod->html_safe($release['name'] ?: $release['tag_name']).'</h5>';
        $res .= '<h6 class="card-subtitle mb-2 text-muted">Version '.$output_mod->html_safe($release['tag_name']).'</h6>';
        if (isset($release['body']) && $release['body']) {
            $res .= '<div class="card-text"><pre class="bg-light p-2">'.$output_mod->html_safe(mb_substr($release['body'], 0, 1000)).'</pre></div>';
        }
        if (isset($release['html_url'])) {
            $res .= '<a href="'.$output_mod->html_safe($release['html_url']).'" target="_blank" rel="noopener" class="btn btn-primary text-white">View Release</a>';
        }
        $res .= '</div></div>';
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_create_delete_event')) {
function github_format_create_delete_event($payload, $type, $output_mod) {
    $ref_type = $payload['ref_type'] ?? 'unknown';
    $ref = $payload['ref'] ?? '';
    $action = $type === 'CreateEvent' ? 'Created' : 'Deleted';
    $class = $type === 'CreateEvent' ? 'success' : 'warning';
    $icon = $type === 'CreateEvent' ? 'bi-plus-circle' : 'bi-dash-circle';

    $res = '<div class="alert alert-'.$class.'">';
    $res .= '<h5><i class="'.$icon.' me-2"></i>'.ucfirst($ref_type).' '.$action.'</h5>';
    $res .= '</div>';

    $res .= '<div class="card">';
    $res .= '<div class="card-body">';
    $clean_ref = html_entity_decode($ref, ENT_QUOTES | ENT_HTML401, 'UTF-8');
    $res .= '<p><strong>'.ucfirst($ref_type).':</strong> <code>'.$output_mod->html_safe($clean_ref).'</code></p>';
    if (isset($payload['description']) && $payload['description']) {
        $clean_desc = html_entity_decode($payload['description'], ENT_QUOTES | ENT_HTML401, 'UTF-8');
        $res .= '<p>'.$output_mod->html_safe($clean_desc).'</p>';
    }
    $res .= '</div></div>';

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_fork_event')) {
function github_format_fork_event($payload, $output_mod) {
    $forkee = $payload['forkee'] ?? array();

    $res = '<div class="alert alert-info">';
    $res .= '<h5><i class="bi bi-diagram-3 me-2"></i>Repository Forked</h5>';
    $res .= '</div>';

    if (!empty($forkee)) {
        $res .= '<div class="card">';
        $res .= '<div class="card-body">';
        $res .= '<h5 class="card-title">'.$output_mod->html_safe($forkee['full_name']).'</h5>';
        if (isset($forkee['description']) && $forkee['description']) {
            $res .= '<p class="card-text">'.$output_mod->html_safe($forkee['description']).'</p>';
        }
        if (isset($forkee['html_url'])) {
            $res .= '<a href="'.$output_mod->html_safe($forkee['html_url']).'" target="_blank" rel="noopener" class="btn btn-primary text-white">View Fork</a>';
        }
        $res .= '</div></div>';
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_watch_event')) {
function github_format_watch_event($payload, $output_mod) {
    $action = $payload['action'] ?? 'started';
    $class = $action === 'started' ? 'success' : 'secondary';

    $res = '<div class="alert alert-'.$class.'">';
    $res .= '<h5><i class="bi bi-eye me-2"></i>'.($action === 'started' ? 'Started Watching' : 'Stopped Watching').'</h5>';
    $res .= '</div>';

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_commit_comment_event')) {
function github_format_commit_comment_event($payload, $output_mod) {
    $comment = $payload['comment'] ?? array();

    $res = '<div class="alert alert-info">';
    $res .= '<h5><i class="bi bi-chat-square-text me-2"></i>Commit Comment</h5>';
    $res .= '</div>';

    if (!empty($comment)) {
        $commit_sha = substr($comment['commit_id'], 0, 7);
        $res .= '<div class="mb-3">';
        $res .= '<h6>Commit: <code>'.$output_mod->html_safe($commit_sha).'</code></h6>';
        $res .= '</div>';

        $res .= '<div class="card">';
        $res .= '<div class="card-body">';
        $res .= '<p class="card-text">'.$output_mod->html_safe(mb_substr($comment['body'], 0, 500)).'</p>';
        if (isset($comment['html_url'])) {
            $res .= '<a href="'.$output_mod->html_safe($comment['html_url']).'" target="_blank" rel="noopener" class="btn btn-primary text-white">View Comment</a>';
        }
        $res .= '</div></div>';
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_wiki_event')) {
function github_format_wiki_event($payload, $output_mod) {
    $pages = $payload['pages'] ?? array();

    $res = '<div class="alert alert-info">';
    $res .= '<h5><i class="bi bi-journal-text me-2"></i>Wiki Updated</h5>';
    $res .= '</div>';

    foreach ($pages as $page) {
        $res .= '<div class="card mb-2">';
        $res .= '<div class="card-body">';
        $res .= '<h6 class="card-title">'.$output_mod->html_safe($page['page_name']).'</h6>';
        $res .= '<p class="card-text"><span class="badge bg-secondary">'.ucfirst($page['action']).'</span></p>';
        if (isset($page['html_url'])) {
            $res .= '<a href="'.$output_mod->html_safe($page['html_url']).'" target="_blank" rel="noopener" class="btn btn-sm btn-primary text-white">View Page</a>';
        }
        $res .= '</div></div>';
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_format_member_event')) {
function github_format_member_event($payload, $output_mod) {
    $member = $payload['member'] ?? array();
    $action = $payload['action'] ?? 'added';

    $res = '<div class="alert alert-success">';
    $res .= '<h5><i class="bi bi-person-plus me-2"></i>Member '.ucfirst($action).'</h5>';
    $res .= '</div>';

    if (!empty($member)) {
        $res .= '<div class="card">';
        $res .= '<div class="card-body">';
        $res .= '<h6 class="card-title">'.$output_mod->html_safe($member['login']).'</h6>';
        if (isset($member['html_url'])) {
            $res .= '<a href="'.$output_mod->html_safe($member['html_url']).'" target="_blank" rel="noopener" class="btn btn-primary text-white">View Profile</a>';
        }
        $res .= '</div></div>';
    }

    return $res;
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_get_event_type_display')) {
function github_get_event_type_display($type) {
    $types = array(
        'PushEvent' => 'Push',
        'IssuesEvent' => 'Issue',
        'IssueCommentEvent' => 'Issue Comment',
        'PullRequestEvent' => 'Pull Request',
        'PullRequestCommentEvent' => 'PR Comment',
        'PullRequestReviewCommentEvent' => 'PR Review Comment',
        'ReleaseEvent' => 'Release',
        'CreateEvent' => 'Create',
        'DeleteEvent' => 'Delete',
        'ForkEvent' => 'Fork',
        'WatchEvent' => 'Watch',
        'CommitCommentEvent' => 'Commit Comment',
        'GollumEvent' => 'Wiki',
        'MemberEvent' => 'Member'
    );

    return isset($types[$type]) ? $types[$type] : preg_replace('/Event$/', '', $type);
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_get_event_icon')) {
function github_get_event_icon($type) {
    $icons = array(
        'PushEvent' => 'bi-arrow-up-circle',
        'IssuesEvent' => 'bi-exclamation-circle',
        'IssueCommentEvent' => 'bi-chat-left-text',
        'PullRequestEvent' => 'bi-git',
        'PullRequestCommentEvent' => 'bi-chat-left-dots',
        'PullRequestReviewCommentEvent' => 'bi-chat-left-dots',
        'ReleaseEvent' => 'bi-tag',
        'CreateEvent' => 'bi-plus-circle',
        'DeleteEvent' => 'bi-dash-circle',
        'ForkEvent' => 'bi-diagram-3',
        'WatchEvent' => 'bi-eye',
        'CommitCommentEvent' => 'bi-chat-square-text',
        'GollumEvent' => 'bi-journal-text',
        'MemberEvent' => 'bi-person-plus'
    );

    return isset($icons[$type]) ? $icons[$type] : 'bi-circle';
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('github_get_event_link')) {
function github_get_event_link($data, $output_mod) {
    $type = $data['type'] ?? '';
    $payload = $data['payload'] ?? array();

    switch ($type) {
        case 'IssuesEvent':
        case 'IssueCommentEvent':
            if (isset($payload['issue']['html_url'])) {
                return '<a href="'.$output_mod->html_safe($payload['issue']['html_url']).'" target="_blank" rel="noopener" class="btn btn-sm btn-primary text-white">View Issue</a>';
            }
            break;

        case 'PullRequestEvent':
        case 'PullRequestCommentEvent':
        case 'PullRequestReviewCommentEvent':
            if (isset($payload['pull_request']['html_url'])) {
                return '<a href="'.$output_mod->html_safe($payload['pull_request']['html_url']).'" target="_blank" rel="noopener" class="btn btn-sm btn-primary text-white">View Pull Request</a>';
            }
            break;

        case 'ReleaseEvent':
            if (isset($payload['release']['html_url'])) {
                return '<a href="'.$output_mod->html_safe($payload['release']['html_url']).'" target="_blank" rel="noopener" class="btn btn-sm btn-primary text-white">View Release</a>';
            }
            break;

        case 'CommitCommentEvent':
            if (isset($payload['comment']['html_url'])) {
                return '<a href="'.$output_mod->html_safe($payload['comment']['html_url']).'" target="_blank" rel="noopener" class="btn btn-sm btn-primary text-white">View Comment</a>';
            }
            break;
    }

    return '';
}}

/**
 * @subpackage github/functions
 */
if (!hm_exists('payload_search')) {
function payload_search($data) {
    $res = array();
    $data_flds = array('url', 'sha', 'body', 'description', 'message', 'name');
    foreach($data as $vals) {
        if (is_array($vals)) {
            $item = array();
            foreach ($data_flds as $fld) {
                if (array_key_exists($fld, $vals)) {
                    $item[$fld] = $vals[$fld];
                }
            }
            if (!empty($item)) {
                if (array_key_exists('html_url', $data)) {
                    $item['html_url'] = $vals['html_url'];
                }
            }
            else {
                $res = array_merge($res, payload_search($vals));
            }
            if (count($item) > 0) {
                $res[] = $item;
            }
        }
    }
    return $res;
}}
